#include "Session.h"
#include "MsgeNode.hpp"
#include "server.h"
#include <boost/asio/detail/socket_ops.hpp>
#include <cstdint>
#include <cstring>
#include <mutex>
#include <sstream>
#include <iomanip>

void Session::start()
{
    _recv_head_msg->Clear();

    auto self = shared_from_this();

    boost::asio::async_read(_socket, boost::asio::buffer(_recv_head_msg->_data, HEAD_LENGTH), 
    [this, self](const boost::system::error_code& error, size_t bytes_transferred) {
        handle_read_head(error, bytes_transferred, self);
    });
}


void Session::handle_read_head(const boost::system::error_code& error, size_t bytes_transferred, std::shared_ptr<Session> self)
{
    if(error)
    {   
        std::cout << "read error\n";
        close();
        _server->delete_session(_uuid);
        return;
    }

    if(bytes_transferred < HEAD_LENGTH)
    {
        std::cout << "read error\n";
        close();
        _server->delete_session(_uuid);
        return;
    }

    uint16_t data_len = 0;
    memcpy(&data_len, _recv_head_msg->_data, HEAD_LENGTH);
    data_len = boost::asio::detail::socket_ops::network_to_host_short(data_len);
    std::cout << "data_len: " << data_len << "\n";

    if(data_len > MAX_LENGTH)
    {
        std::cout << "data_len error\n";
        close();
        _server->delete_session(_uuid);
        return;
    }

    _recv__msg = std::make_shared<MsgNode>(data_len);
    boost::asio::async_read(_socket, boost::asio::buffer(_recv__msg->_data, data_len), 
    [this, self](const boost::system::error_code& error, size_t bytes_transferred) {
        handle_read_msg(error, bytes_transferred, self);
    });
}

void Session::handle_read_msg(const boost::system::error_code& error, size_t bytes_transferred, std::shared_ptr<Session> self)
{
    if(error)
    {
        std::cout << "read error\n";
        close();
        _server->delete_session(_uuid);
        return;
    }

    _recv__msg->_data[_recv__msg->_total_len] = '\0';
    std::cout << "msg: " << _recv__msg->_data << "\n";
    send(_recv__msg->_data, _recv__msg->_total_len);

    _recv__msg->Clear();
    boost::asio::async_read(_socket, boost::asio::buffer(_recv_head_msg->_data, HEAD_LENGTH), 
    [this, self](const boost::system::error_code& error, size_t bytes_transferred) {
        handle_read_head(error, bytes_transferred, self);
    });
}


void Session::handle_write(const boost::system::error_code& error, size_t bytes_transferred, std::shared_ptr<Session> self)
{
    if(error)
    {
        std::cout << "write error\n";
        _server->delete_session(_uuid);
        return;
    }

    std::unique_lock<std::mutex> lock(_send_queue_lock);
    
    _send_queue.pop();
    if(_send_queue.empty())
    {
        return;
    }

    auto& msg = _send_queue.front();

    _recv_head_msg->Clear();
    boost::asio::async_write(_socket, boost::asio::buffer(msg->_data, msg->_total_len), 
    [this, self](const boost::system::error_code& error, std::size_t bytes_transferred) {
        handle_write(error, bytes_transferred, self);
    });
}


void Session::send(const char* msg, size_t len)
{
    bool pending = false;
    std::unique_lock<std::mutex> lock(_send_queue_lock);
    if(!_send_queue.empty())
    {
        pending = true;
    }

    std::shared_ptr<MsgNode> node = std::make_shared<MsgNode>(msg, len);
    _send_queue.push(node);
    
    if(pending)
    {
        return;
    }

    auto self = shared_from_this();
    boost::asio::async_write(_socket, boost::asio::buffer(node->_data, node->_total_len), 
    [this, self](const boost::system::error_code& error, std::size_t bytes_transferred) {
        handle_write(error, bytes_transferred, self);
    });
}

void Session::print_hex(const char* data, size_t len)
{
    std::stringstream oss;
    oss << "0x";
    for(size_t i = 0; i < len; ++i)
    {
        std::string hex_str;
        oss << std::hex << std::setw(2) << std::setfill('0') << (int)data[i] << " ";
    }
    std::cout << oss.str() << "\n";
}